﻿using System.Collections.Generic;
using SpiceNet.Circuits;
using SpiceNet.Diagnostics;
using SpiceNet.Parameters;

namespace SpiceNet.Models
{
    /// <summary>
    /// A class that represents a capacitor model
    /// </summary>
    public class CapacitorModel : Model
    {
        #region Parameters
        private enum _c
        {
            CAP_MOD_CJ = 101, CAP_MOD_CJSW = 102, CAP_MOD_DEFWIDTH = 103, CAP_MOD_C = 104, CAP_MOD_NARROW = 105
        }
        private static Dictionary<string, IP> _pTable = new Dictionary<string, IP>
        {
            { "cj", new IP(IF.IOPA, (int)_c.CAP_MOD_CJ, T.REAL, "Bottom Capacitance per area") },
            { "cjsw", new IP(IF.IOPA, (int)_c.CAP_MOD_CJSW, T.REAL, "Sidewall capacitance per meter") },
            { "defw", new IP(IF.IOPX, (int)_c.CAP_MOD_DEFWIDTH, T.REAL, "Default width") },
            { "c", new IP(IF.IP, (int)_c.CAP_MOD_C, T.FLAG, "Capacitor model") },
            { "narrow", new IP(IF.IOPA, (int)_c.CAP_MOD_NARROW, T.REAL, "width correction factor") }
        };

        /// <summary>
        /// Get the parameters
        /// </summary>
        public override Dictionary<string, IP> ParamTable => _pTable;

        public Parameter<double> CAPcj { get; } = new Parameter<double>();
        public Parameter<double> CAPcjsw { get; } = new Parameter<double>();
        public Parameter<double> CAPdefWidth { get; } = new Parameter<double>(10.0e-6);
        public Parameter<double> CAPnarrow { get; } = new Parameter<double>();
        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        public CapacitorModel(string name) : base(name) { }

        /// <summary>
        /// Set parameters
        /// </summary>
        protected override void Param(int id, object value, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.CAP_MOD_CJ:
                    CAPcj.Par((double)value);
                    break;
                case _c.CAP_MOD_CJSW:
                    CAPcjsw.Par((double)value);
                    break;
                case _c.CAP_MOD_DEFWIDTH:
                    CAPdefWidth.Par((double)value);
                    break;
                case _c.CAP_MOD_NARROW:
                    CAPnarrow.Par((double)value);
                    break;
                case _c.CAP_MOD_C:
                    /* just being reassured by the user that we are a capacitor */
                    /* no-op */
                    break;
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Ask parameters
        /// </summary>
        protected override object Ask(int id, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.CAP_MOD_CJ:
                    return CAPcj.Value;
                case _c.CAP_MOD_CJSW:
                    return CAPcjsw.Value;
                case _c.CAP_MOD_DEFWIDTH:
                    return CAPdefWidth.Value;
                case _c.CAP_MOD_NARROW:
                    return CAPnarrow.Value;
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Load the circuit with instance of this model
        /// </summary>
        /// <param name="ckt"></param>
        public override void Load(Circuit ckt)
        {
            bool cond1 = false;

            // Actually, this part is the same for all capacitor models as well, perhaps it would be possible to optimize this if necessary?
            // Check if the capacitors are in the circuit or are open-circuited
            if ((ckt.Mode & (Circuit.Modes.Tran | Circuit.Modes.Ac | Circuit.Modes.TranOp)) != 0)
            {
                /* evaluate device independent analysis conditions */
                cond1 =
                    (((ckt.Mode.HasFlag(Circuit.Modes.Dc)) &&
                        (ckt.Mode.HasFlag(Circuit.Modes.InitJct)))
                    || ((ckt.Mode.HasFlag(Circuit.Modes.Uic)) &&
                        (ckt.Mode.HasFlag(Circuit.Modes.InitTran))));
            }

            // Loop through all capacitors
            foreach (Instance inst in Instances)
                inst.Load(ckt, this, cond1);
        }
    }
}
